package br.unb.midialab.pianogame.timer;

import java.util.ArrayList;

/**
 * A metronome.
 * 
 * @author Dave Briccetti
 */
public class Metronome {

	private Thread thread; // New thread each time the metronome is turned on
	private final Runnable runnable = createRunnable();
	private static long timeBetweenTicks;

	public static long getTimeBetweenTicks() {
		return timeBetweenTicks;
	}

	protected boolean keepPlaying;
	protected boolean pause;
	private static ArrayList<Metronome_Observer_IF> listeners;

	public Metronome(int bpm, int resolution) {
		timeBetweenTicks = (1000 * 60 / bpm) / resolution;
		setup();
		keepPlaying = false;
		pause = false;
		startThread();
	}

	private static void setup() {
		listeners = new ArrayList<Metronome_Observer_IF>();
	}

	private void notifyListeners(long tick) {
		java.util.Iterator<Metronome_Observer_IF> it = listeners.iterator();

		while (it.hasNext()) {
			Metronome_Observer_IF nf = (Metronome_Observer_IF) it.next();
			nf.update(tick);
		}
	}

	public void addListener(Metronome_Observer_IF listener) {
		listeners.add(listener);
	}

	public void removeListener(Metronome_Observer_IF listener) {
		listeners.remove(listener);
	}

	/**
	 * Stops the metronome.
	 */
	public void stop() {
		keepPlaying = false;
		if (thread != null) {
			if(thread.getState()==Thread.State.TIMED_WAITING)
				thread.notify();
			thread.interrupt(); // Interrupt the sleep
		}
		
	}

	private Runnable createRunnable() {
		return new Runnable() {

			public void run() {
				final long startTime = System.currentTimeMillis();
				int tick = 0;

				while (keepPlaying) {
					if (!pause) {
						notifyListeners(tick++);

						try {
							Thread.sleep(timeBetweenTicks);
						} catch (InterruptedException ex) {
							ex.printStackTrace();
						}
					}
				}
			}
		};
	}

	protected void startThread() {
		thread = new Thread(runnable, "Metronome");
		thread.setPriority(Thread.MAX_PRIORITY);
		thread.start();
	}

	public boolean isKeepPlaying() {
		return keepPlaying;
	}

	public void setKeepPlaying(boolean keepPlaying) {
		this.keepPlaying = keepPlaying;
	}

	public void pause() {
		pause = !pause;
	}

	public void restart() {
		setup();
		startThread();
	}

}
